package pl.enigmatic.reflection;

public class Accessor implements Getter, Setter {

	public static Accessor createAccessor(final Getter getter, final Setter setter) {
		return new Accessor(getter, setter);
	}

	public static Accessor createSimpleAccessor(final Class<?> cls, final String property) throws SecurityException, NoSuchMethodException {
		Getter getter = new SimpleGetter(cls, property);
		Setter setter = new SimpleSetter(cls, property, getter.getType());
		return createAccessor(getter, setter);
	}

	public static Accessor createPublicPropertyAccessor(final Class<?> cls, final String property) throws SecurityException, NoSuchFieldException {
		Getter getter = new PublicPropertyGetter(cls, property);
		Setter setter = new PublicPropertySetter(cls, property);
		return createAccessor(getter, setter);
	}

	public static Accessor createNestedPropertyAccessor(final Class<?> cls, final PropertyPath path, final Creator creator) throws Exception {
		Getter getter = new NestedPropertyGetter(cls, path) {

			@Override
			protected Getter createGetter(Class<?> cls, String property) {
				return creator != null ? creator.createGetter(cls, property) : null;
			}
		};
		Setter setter = new NestedPropertySetter(cls, path, getter.getType()) {

			@Override
			protected Getter createGetter(Class<?> cls, String property) {
				return creator != null ? creator.createGetter(cls, property) : null;
			}

			@Override
			protected Setter createSetter(Class<?> cls, String property, Class<?> type) {
				return creator != null ? creator.createSetter(cls, property, type) : null;
			}
		};
		return createAccessor(getter, setter);
	}

	public static Accessor createNestedPropertyAccessor(final Class<?> cls, final PropertyPath path) throws Exception {
		return createNestedPropertyAccessor(cls, path, null);
	}

	public static interface Creator {

		public Getter createGetter(Class<?> cls, String property);

		public Setter createSetter(Class<?> cls, String property, Class<?> type);
	}

	private final Getter getter;
	private final Setter setter;

	protected Accessor(final Getter getter, final Setter setter) {
		this.getter = getter;
		this.setter = setter;
	}

	@Override
	public Object get(final Object target) {
		return getter.get(target);
	}

	@Override
	public boolean set(final Object target, final Object value) {
		return setter.set(target, value);
	}

	@Override
	public Class<?> getType() {
		return getter.getType();
	}

	public PropertyPath getPath() {
		return getter.getPath();
	}
}
